#include "thread.h"
#include "memory.h"
#include "string.h"
#include "print.h"
#include "debug.h"
#include "interrupt.h"
#include "process.h"

#define PG_SIZE         4096

struct task_struct* main_thread;
struct list ready_list;
struct list all_list;



static void kernel_thread(thread_func* func, void* func_arg){
    intr_enable();
    func(func_arg);
}

void thread_create(struct task_struct* pthread, thread_func function, void* func_arg){
    pthread->self_kstack -= sizeof(struct intr_stack);
    /* 先把栈指针减去线程栈结构体大小 */
    pthread->self_kstack -= sizeof(struct thread_stack);
    /* 由于我们的结构体是由低到高发展的，所以先减去结构体大小
     * 将这个低地址作为结构体起始地址
     */
    struct thread_stack* _thread_stack = (struct thread_stack*)pthread->self_kstack;

    _thread_stack->eip = kernel_thread;
    _thread_stack->func = function;
    _thread_stack->func_arg = func_arg;
    _thread_stack->ebp = _thread_stack->ebx = _thread_stack->edi \
    = _thread_stack->esi = 0;
}
void init_thread(struct task_struct* pthread, char* name, int prio){
    /* 将整个pcb内容清零 */
    memset(pthread, 0, sizeof(struct task_struct));
    /* 复制名字 */
    strcpy(pthread->name, name);
    /* 设置线程优先级 */
    pthread->priority = prio;
    /* 设置线程的内核栈 */
    pthread->self_kstack = (uint32_t*)((uint32_t)pthread + PG_SIZE);
    /* 如果是主线程pcb 则设置为运行态 否则设置为就绪态 */
    if(pthread == main_thread){
        pthread->status = TASK_RUNNING;
    }else{
        pthread->status = TASK_READY;
    }
    /* 没有什么实际意义，用来调度的时候检查这个值是否被覆盖 */
    pthread->magic_stack_limitation = 0x20030528;

    /* 线程的时间片根据线程优先级决定 */
    pthread->remainning_ticks = pthread->priority;
    pthread->ticks = 0;
    
    pthread->pgdir = NULL;
}

static void make_main_thread_pcb(void){
    main_thread = running_task();

    /* 初始化线程pcb */
    init_thread(main_thread, "main", 31);

    ASSERT(!(elem_find(&all_list, &main_thread->all_tag)));
    /* 追加到全部线程链表 */
    list_append(&all_list, &main_thread->all_tag);
}

struct task_struct* thread_start(char* name, int prio, thread_func function, void* func_arg){
    /* 我们系统设计的比较小 所以用一页大小保存pcb就可以了 */
    struct task_struct* _pcb = get_kernel_pages(1);
    if(_pcb == NULL){
        return NULL;
    }
    init_thread(_pcb, name, prio);
    thread_create(_pcb, function, func_arg);

    ASSERT(!(elem_find(&all_list, &_pcb->all_tag)));
    /* 追加到全部线程链表 */
    list_append(&all_list, &_pcb->all_tag);

    ASSERT(!(elem_find(&ready_list, &_pcb->general_tag)));
    /* 追加到就绪线程链表 */
    list_append(&ready_list, &_pcb->general_tag);
    // /* 弹出栈中的abi协议固定的寄存器后使用ret弹出eip的值，此时eip指向kernel_thread */
    // asm volatile("mov %0, %%esp; pop %%ebp; pop %%ebx; pop %%edi; pop %%esi; ret" : : "g"(_pcb->self_kstack) : "memory");
    return _pcb;
}

struct task_struct* running_task(void){
   uint32_t esp; 
   asm ("mov %%esp, %0" : "=g" (esp));
  /* 取esp整数部分即pcb起始地址 */
   return (struct task_struct*)(esp & 0xfffff000);
}

extern void switch_to(struct task_struct* cur, struct task_struct* next);
void schedule(void){
    struct list_elem* thread_tag;
    ASSERT(intr_get_status() == INTR_OFF);
        
    struct task_struct* cur = running_task(); 
    if (cur->status == TASK_RUNNING) { // 若此线程只是cpu时间片到了,将其加入到就绪队列尾
        ASSERT(!elem_find(&ready_list, &cur->general_tag));
        list_append(&ready_list, &cur->general_tag);
        cur->ticks = cur->priority;     // 重新将当前线程的ticks再重置为其priority;
        cur->status = TASK_READY;
    } else { 
        /* 若此线程需要某事件发生后才能继续上cpu运行,
        不需要将其加入队列,因为当前线程不在就绪队列中。*/
    }

    ASSERT(!list_empty(&ready_list));
    thread_tag = NULL;	  // thread_tag清空
    /* 将thread_ready_list队列中的第一个就绪线程弹出,准备将其调度上cpu. */
    thread_tag = list_pop(&ready_list);   
    struct task_struct* next = elem2entry(struct task_struct, general_tag, thread_tag);
    next->status = TASK_RUNNING;

    process_activate(next);

    switch_to(cur, next);
}

void thread_block(enum task_status status){
    /* 要求锁定状态是这几个 否则挂起系统 */
    ASSERT((status == TASK_BLOCKED) || (status == TASK_WAITING) || (status == TASK_HANGING));
    enum intr_status _old_intr_status = intr_disable();
    /* 获取当前线程pcb */
    struct task_struct* _pcb = running_task();
    _pcb->status = status;
    schedule();

    /* 这个intr_set_status会在线程解锁后执行 */
    intr_set_status(_old_intr_status);
}

void thread_unblock(struct task_struct* pcb){
    enum intr_status _old_intr_status = intr_disable();
    /* 要求解锁的线程是这几个状态 */
    ASSERT((pcb->status == TASK_BLOCKED) || (pcb->status == TASK_WAITING) || (pcb->status == TASK_HANGING));
    /* 要求就绪链表中没有该线程 */
    ASSERT(!elem_find(&ready_list, &pcb->general_tag));
    /* 设置状态为就绪态 */
    pcb->status = TASK_READY;
    /* 将该线程放入就绪链表头 得到最优先机会执行 */
    list_push(&ready_list, &pcb->general_tag);

    intr_set_status(_old_intr_status);
}

void thread_init(void){
    put_str("   thread init start\n");

    list_init(&ready_list);
    list_init(&all_list);
    make_main_thread_pcb();

    put_str("   thread init done\n\n");
}